package com.learning.spring.cloud.alibaba.sentinel.order.web.rule;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * ClassName: RuleDefination
 * Description:
 * Date: 2021/9/3 9:29 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Component
public class RuleDefinition {

    public RuleDefinition() {
        initSystemRule();
        initFlowRules();
    }

    /**
     * 系统保护规则
     * <p>
     * 系统保护规则是应用整体维度的，而不是资源维度的，并且仅对入口流量生效。
     * 入口流量指的是进入应用的流量（EntryType.IN），比如 Web 服务或 Dubbo 服务端接收的请求，都属于入口流量。
     */
    private void initSystemRule() {
        SystemRule rule = new SystemRule();
        rule.setLimitApp("default");
        // Load 自适应（仅对 Linux/Unix-like 机器生效）：系统的 load1 作为启发指标，进行自适应系统保护。
        // 当系统 load1 超过设定的启发值，且系统当前的并发线程数超过估算的系统容量时才会触发系统保护（BBR 阶段）。
        // 系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
        rule.setHighestSystemLoad(10);
        // 入口 QPS：当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
        rule.setQps(1);
        // 平均 RT：当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护，单位是毫秒。
        rule.setAvgRt(1);
        // 并发线程数：当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
        rule.setMaxThread(128);
        // 当系统 CPU 使用率超过阈值即触发系统保护（取值范围 0.0-1.0），比较灵敏
        rule.setHighestCpuUsage(0.8);

        final ArrayList<SystemRule> systemRules = Lists.newArrayList(rule);
        SystemRuleManager.loadRules(systemRules);
    }

    /**
     * 定义限流规则
     */
    private void initFlowRules() {
        FlowRule rule = new FlowRule();
        // 资源名，即限流规则的作用对象
        rule.setRefResource(ResourceName.ORDER_GET);
        // 限流阈值类型（QPS 或并发线程数）
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 直接：当api大达到限流条件时，直接限流
        rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        // 限流阈值类型（QPS 或并发线程数）
        rule.setCount(1);
        // 流控针对的调用来源（微服务名），若为 default 则不区分调用来源
        rule.setLimitApp("default");
        // 流量控制效果（直接拒绝、Warm Up、匀速排队）
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);

        final ArrayList<FlowRule> flowRules = Lists.newArrayList(rule);
        FlowRuleManager.loadRules(flowRules);
    }

    /**
     * 熔断规则
     * <p>
     * 10s内调用接口  进行熔断
     */
    private void initDegradeRule() {
        DegradeRule rule = new DegradeRule();
        // 资源名，即规则的作用对象
        rule.setResource(ResourceName.ORDER_GET);
        // 熔断策略，支持慢调用比例/异常比例/异常数策略
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        // 慢调用比例模式下为慢调用临界 RT（超出该值计为慢调用）；异常比例/异常数模式下为对应的阈值
        rule.setCount(0.2);
        // 熔断时长，单位为 s
        rule.setTimeWindow(10);
        // 熔断触发的最小请求数，请求数小于该值时即使异常比率超出阈值也不会熔断（1.7.0 引入）
        rule.setMinRequestAmount(RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT);
        // 统计时长（单位为 ms），如 60*1000 代表分钟级（1.8.0 引入）
        rule.setStatIntervalMs(1000);
        // 慢调用比例阈值，仅慢调用比例模式有效（1.8.0 引入）
        rule.setSlowRatioThreshold(1.0d);

        final ArrayList<DegradeRule> degradeRules = Lists.newArrayList(rule);
        DegradeRuleManager.loadRules(degradeRules);
    }


    /**
     * 授权规则
     */
    private void initAuthorityRule() {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource(ResourceName.ORDER_GET);
        // 限制模式，AUTHORITY_WHITE 为白名单模式，AUTHORITY_BLACK 为黑名单模式，默认为白名单模式
        // 比如我们希望控制对资源 test 的访问设置白名单，只有来源为 appA 和 appB 的请求才可通过，则可以配置规则
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        // 对应的黑名单/白名单，不同 origin 用 , 分隔，如 appA,appB
        rule.setLimitApp("appA,appB");
        final ArrayList<AuthorityRule> authorityRules = Lists.newArrayList(rule);
        AuthorityRuleManager.loadRules(authorityRules);
    }

    /**
     * 热点规则
     */
    private void initParamFlowRule() {
        ParamFlowRule rule = new ParamFlowRule();
        final ArrayList<ParamFlowRule> paramFlowRules = Lists.newArrayList(rule);
        ParamFlowRuleManager.loadRules(paramFlowRules);
    }


}
